<?php
/**
 * Project: AIGC
 * Author: moufer <moufer@163.com>
 * Date: 2024/10/11 21:52
 */

namespace module\ai\library\channel;

use Generator;
use OpenAI\Contracts\ResponseContract;
use OpenAI\Responses\Chat\CreateStreamedResponse;
use \module\ai\library\OpenAI\CreateStreamedResponse as CreateStreamedResponse2;

class Completion
{

    protected bool  $isStream;
    protected array $data;

    protected bool $stop = false;

    public static function make(ResponseContract $response): static
    {
        $rawData = $response->toArray();
        if ($response instanceof CreateStreamedResponse || $response instanceof CreateStreamedResponse2) {
            if (isset($response->choices[0])) {
                $rawData['choices'][0]['message'] = $response->choices[0]?->delta->toArray();
            } else if ($response->usage) {
                $rawData['choices'][0]['finish_reason'] = 'stop';
            }
        }
        return new static($rawData);
    }

    public function __construct(protected array $rawData)
    {
        $this->data = [
            'index'         => $this->rawData['choices'][0]['index'] ?? 0,
            'content'       => $this->rawData['choices'][0]['message']['content'] ?? '',
            'usage'         => $this->rawData['usage'] ?? null,
            'finish_reason' => $this->rawData['choices'][0]['finish_reason'] ?? null,
        ];
        $this->data['status'] = empty($this->data['finish_reason']) ? 'receiving' : 'finish';
        $this->isStream = isset($rawData['choices'][0]['delta']);
    }

    public function withFinishReason(string $status): static
    {
        $this->data['finish_reason'] = $status;
        $this->data['status'] = $status ? 'finish' : 'receiving';
        return $this;
    }

    public function merge(Completion $completion): static
    {
        $content = $this->data['content'];
        $myData = $this->data;
        $data = $completion->data();
        $tmp = array_merge($myData, $data);

        //流式对话时，回答内容拼接，否则就是直接覆盖
        if ($this->isStream && empty($data['clear_receive'])) {
            $tmp['content'] = $content . $data['content'];
        }

        $this->data = $tmp;
        return $this;
    }

    public function sse(bool $json = true): string|array
    {
        $data = $this->data;
        if (env('app_debug')) {
            $data['raw'] = $this->rawData;
        }

        return $json ? json_encode($data, JSON_UNESCAPED_UNICODE) : $data;
    }

    /**
     * @return Generator
     */
    public function sseChunks(): Generator
    {
        $sse = $this->sse(false);
        if (empty($sse['content']) || $this->status() === 'finish') {
            yield json_encode($sse, JSON_UNESCAPED_UNICODE);
        } else {
            $contents = mb_str_split($sse['content']);
            $length = count($contents);
            $loop = 0;
            foreach ($contents as $content) {
                $data = array_merge($sse, ['content' => $content]);
                //如果是最后一次循环，就设置结束状态
                if ($data['status'] === 'finish' && $loop < $length - 1) {
                    $data['status'] = 'receiving';
                }
                //$data['cuk_index'] = $loop++;
                yield json_encode($data, JSON_UNESCAPED_UNICODE);
            }
        }
    }

    public function data(): array
    {
        return $this->data;
    }

    public function tokens(string $type): int
    {
        return $this->data['usage'][$type] ?? 0;
    }

    public function message(): string
    {
        return $this->data['content'] ?? '';
    }

    public function status(): string
    {
        $finishReason = $this->data['finish_reason'] ?? '';
        return ($finishReason) ? 'finish' : 'receiving';
    }

    public function finishReason(): string
    {
        return $this->data['finish_reason'] ?? '';
    }

    public function index(): int
    {
        return $this->data['index'] ?? 0;
    }

    public function __toString(): string
    {
        return json_encode($this->data, JSON_UNESCAPED_UNICODE);
    }
}